%{
/***************************************************************************
 *
 * $Id: language.y 17 2009-06-29 21:44:54Z Michael.McTernan $
 *
 * This file is part of tdgen, a timing diagram renderer.
 * Copyright (C) 2005 Michael C McTernan, Michael.McTernan.2001@cs.bris.ac.uk
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 **************************************************************************/

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "safe.h"
#include "tig.h"

/* Lexer prototype to prevent compiler warning */
int yylex(void);

/* Use verbose error reporting such that the expected token names are dumped */
#define YYERROR_VERBOSE

/* Name of parameter that is passed to yyparse() */
#define YYPARSE_PARAM yyparse_result

extern FILE *yyin;
extern int   yyparse (void *YYPARSE_PARAM);

extern unsigned long lex_linenum;

/* yyerror
 *  Error handling function.  The TOK_XXX names are substituted for more
 *  understandable values that make more sense to the user.
 */
void yyerror(const char *str)
{
    static const char *tokNames[] = { "TOK_TIMG",        "TOK_STRING",
                                      "TOK_OBRACE",      "TOK_CBRACE",
                                      "TOK_SEMICOLON",   "TOK_OSBRACKET",
                                      "TOK_CSBRACKET",   "TOK_EQUAL",
                                      "TOK_COMMA",       "TOK_ATTR_COLOUR",
                                      "TOK_ATTR_URL",	 "TOK_GATTR_DIVS" };

    static const char *tokRepl[] =  { "tdgen",          "string",
                                      "{",               "}",
                                      ";",               "[",
                                      "]",               "=",
                                      ",",               "colour",
                                      "url",             "divs" };
    static const int tokArrayLen = sizeof(tokNames) / sizeof(char *);

    char *s;
    int   t;

    /* Print standard message part */
    fprintf(stderr,"Error detected at line %lu: ", lex_linenum);

    /* Search for TOK */
    s = strstr(str, "TOK_");
    while(s != NULL)
    {
        int found = 0;

        /* Print out message until start of the token is found */
        while(str < s)
        {
            fprintf(stderr, "%c", *str);
            str++;
        }

        /* Look for the token name */
        for(t = 0; t < tokArrayLen && !found; t++)
        {
            if(strncmp(tokNames[t], str, strlen(tokNames[t])) == 0)
            {
                /* Dump the replacement string */
                fprintf(stderr, "'%s'", tokRepl[t]);

                /* Skip the token name */
                str += strlen(tokNames[t]);

                /* Exit the loop */
                found = 1;
            }
        }

        /* Check if a replacement was found */
        if(!found)
        {
            /* Dump the next char and skip it so that TOK doesn't match again */
            fprintf(stderr, "%c", *str);
            str++;
        }

        s = strstr(str, "TOK_");
    }

    fprintf(stderr, "%s.\n", str);

}


const char *removeEscapes(const char *in)
{
    const uint16_t l = strlen(in);
    char          *r = malloc(l + 1);
    uint16_t       t, u;

    if(r != NULL)
    {
        for(t = u = 0; t < l; t++)
        {
            r[u] = in[t];
            if(in[t] != '\\' || in[t + 1] != '\"')
            {
                u++;
            }
        }

        r[u] = '\0';
    }

    return r;
}


tig_t *TigParse(FILE *f)
{
    tig_t *r = zalloc_s(sizeof(tig_t));

    yyin = f;

    /* Parse, and check that no errors are found */
    if(yyparse(r) == 0)
    {
    	return r;
    }
    else
    {
        return NULL;
    }
}


%}

%union
{
    char           *string;
    tig_wave_t	   *wave;
    tig_attr_t      attr;
    tig_attrlist_t *attrList;
};

%type <wave>       wave line linelist tdgen
%type <attr>       attr gattr TOK_ATTR_COLOUR TOK_ATTR_URL TOK_GATTR_DIVS
%type <attrList>   attrlist attrdef gattrlist gattrdef
%type <string>     TOK_STRING


%token TOK_TIMG TOK_STRING TOK_OBRACE TOK_CBRACE TOK_SEMICOLON
       TOK_OSBRACKET TOK_CSBRACKET TOK_EQUAL TOK_COMMA
       TOK_ATTR_COLOUR TOK_ATTR_URL TOK_GATTR_DIVS

%%
tdgen:    TOK_TIMG TOK_OBRACE TOK_OSBRACKET gattrlist TOK_CSBRACKET TOK_SEMICOLON linelist TOK_SEMICOLON TOK_CBRACE
{
	((tig_t *)YYPARSE_PARAM)->gattr = $4;
	((tig_t *)YYPARSE_PARAM)->waves = $7;

};

	  | TOK_TIMG TOK_OBRACE linelist TOK_SEMICOLON TOK_CBRACE
{
	((tig_t *)YYPARSE_PARAM)->waves = $3;
};

gattrlist: gattrdef
	 | gattrlist TOK_COMMA gattrdef
{
	$$ = TigAttrLink($1, $3);
};


gattrdef:  gattr TOK_EQUAL TOK_STRING
{
	$$ = TigAttrAlloc($1, $3);
};


gattr: TOK_GATTR_DIVS;


linelist:  line
           | linelist TOK_SEMICOLON line
{
	$$ = TigWaveLink($1, $3);
};


line:      wave |
           wave TOK_OSBRACKET attrlist TOK_CSBRACKET
{
	$$ = TigWaveLinkAttr($1, $3);
};

wave:      TOK_STRING TOK_STRING
{
	$$ = TigWaveAlloc($1, $2);
};

attrlist: attrdef
	 | attrlist TOK_COMMA attrdef
{
	$$ = TigAttrLink($1, $3);
};

attrdef:  attr TOK_EQUAL TOK_STRING
{
	$$ = TigAttrAlloc($1, $3);
};

attr: TOK_ATTR_COLOUR | TOK_ATTR_URL;

%%


/* END OF FILE */
